// int shmget(key_t key, size_t size, int shmflg);
// key: 用户形成，需要具有唯一性，是内核使用的一个字段，内核通过key进行区分shm的唯一性，但用户不能通过key来进行shm的管理
// size--->shm大小
// shmflg: IPC_CREAT and IPC_EXCL
// IPC_CREAT: 如果要创建的共享内存不存在，就创建它，如果存在，获取该共享内存并返回
// IPC_EXCL: 单独使用没有意义
// IPC_CREAT|IPC_EXCL: 如果要创建的共享内存不存在，就创建它，如果存在，出错返回 -->成功返回意味着这个shm是全新的
// shmget的返回值: 内核给用户返回的一个标识符，用来进行用户级对共享内存管理的id值

#ifndef __SHM_HPP__
#define __SHM_HPP__

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <string>
#include <cerrno>
#include <cstdio>
#include <unistd.h>
#include <cstring>

using namespace std;

const int Creater = 1;
const int User = 2;
const int gproj_id = 0x66;
const int gShmSize = 4096;
const string gpathname = "/home/yhr/ubuntu/shm";

class Shm
{
private:
    // 获取key值
    key_t GetCommKey()
    {
        key_t k = ftok(_pathname.c_str(), _proj_id);
        if (k < 0)
        {
            perror("ftok");
        }
        return k;
    }
    // 获取共享内存
    int GetShmHelper(key_t key, int size, int flag)
    {

        int shmid = shmget(key, size, flag);
        if (shmid < 0)
        {
            perror("ShmGet");
        }

        return shmid;
    }

    string RoleToString(int who)
    {
        if (who == Creater)
            return "Creater";
        else if (who == User)
            return "User";
        else
            return "None";
    }

    void *AttachShm()
    {
        if (_addrshm != nullptr)
            DetachShm(_addrshm);
        void *shmaddr = shmat(_shmid, nullptr, 0);
        if (shmaddr == nullptr)
        {
            perror("shmaddr");
        }
        cout << RoleToString(_who) << " attach shm ..." << endl;
        return shmaddr;
    }

    void DetachShm(void *shmaddr)
    {
        if (shmaddr == nullptr)
            return;
        shmdt(shmaddr);
        cout << RoleToString(_who) << " detach shm ..." << endl;
    }

public:
    Shm(const string pathname, const int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr)
    {
        _key = GetCommKey();
        if (who == Creater)
            GetShmForCreate();
        else if (who == User)
            GetShmForUse();
        _addrshm = AttachShm();

        cout << "key: " << ToHex(_key) << endl;
        cout << "shmid: " << _shmid << endl;
    }
    ~Shm()
    {

        if (_who == Creater)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr);
        }

        cout << "shm remove done ..." << endl;
    }

    // 将key值转换为十六进制
    string ToHex(key_t key)
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }

    // Creater获取共享内存
    bool GetShmForCreate()
    {
        if (_who == Creater)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
            if (_shmid >= 0)
            {
                cout << "shm create done ..." << endl;
                sleep(5);
                return true;
            }
        }
        return false;
    }

    // User获取共享内存
    bool GetShmForUse()
    {
        if (_who == User)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
            if (_shmid >= 0)
            {
                cout << "shm create done ..." << endl;
                sleep(5);
                return true;
            }
        }
        return false;
    }

    void *Addr()
    {
        return _addrshm;
    }

    void Zero()
    {
        if (_addrshm)
        {
            memset(_addrshm, 0, gShmSize);
        }
    }

    // 获取共享内存属性
    void DeBugShm()
    {
        struct shmid_ds ds;
        int n = shmctl(_shmid, IPC_STAT, &ds);
        if (n < 0)
            return;
        cout << "ds.shm_perm.__key: " << ds.shm_perm.__key << endl;
        cout << "ds.shm_nattch: " << ds.shm_nattch << endl;
    }

private:
    key_t _key;
    string _pathname;
    int _shmid;
    int _proj_id;
    int _who;
    void *_addrshm;
};

#endif